/*
 * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "freertos/timers.h"

#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_bridge.h"
#include "esp_eth.h"
#include "lwip/netif.h"

#include "usbh_modem_board.h"
#include "usbh_modem_wifi.h"
#ifdef CONFIG_EXAMPLE_ENABLE_WEB_ROUTER
#include "modem_http_config.h"
#endif

#ifdef CONFIG_EXAMPLE_PING_NETWORK
#include "ping/ping_sock.h"
#endif

#include "board_common.h"
#include "iot_board.h"
#include "driver_adc.h"

#include "mesh_utils.h"
#include "mesh_router_common.h"
#include "mlink.h"
#include "mlink_ota.h"
#include "mlink_mqtt_handle.h"
#include "mesh_mqtt_handle.h"
#include "mlink_dt_handle.h"
#include "mlink_lora_handle.h"
#include "mlink_sntp_handle.h"
#include "device_controller.h"
#include "mlink_subdevice.h"
#include "mb_app.h"
#include "lk8563.h"
#include "mupgrade.h"
#include "E32_433T33S.h"
#include "app_sntp.h"
#include "wifi_http_config.h"

#define TAG "app_main"
#define CONFIG_DUMP_SYSTEM_STATUS
// #define CONFIG_EXAMPLE_PING_NETWORK

#define EVENT_GROUP_MLINK_SET_STATUS  BIT0
#define EVENT_GROUP_MLINK_REPORT_STATUS  BIT1
#define EVENT_GROUP_APP_TICK          BIT2
#define EVENT_GROUP_AI_SAMPLE          BIT3
#define EVENT_GROUP_ETH_GOT_IP          BIT4
#define EVENT_GROUP_STA_GOT_IP          BIT5

typedef struct report_cid_data {
    subdev_port_t *subdev_port;
    subdev_cid_t *subdev_cid;
} report_cid_data_t;

static esp_netif_t *gateway_default_netif = NULL;
static EventGroupHandle_t g_event_group = NULL;
static QueueHandle_t g_report_cid_queue = NULL;

static modem_wifi_config_t s_modem_wifi_config = MODEM_WIFI_DEFAULT_CONFIG();
static bool g_e32_aux_status = false;
/*
    @brief 写入cid到上报队列
    @param subdev_cid 待上报的cid数据
    @param timeout 超时时间
    @return 0: 成功, -1: 失败
*/
int write_cid_to_report_queue(subdev_port_t *subdev_port, subdev_cid_t *subdev_cid, int timeout) {
    if(g_report_cid_queue == NULL) {
        return -1;
    }
    report_cid_data_t subdev_cid_data = {
        .subdev_port = subdev_port,
       .subdev_cid = subdev_cid,
    };
    if(xQueueSend(g_report_cid_queue, &subdev_cid_data, timeout)!= pdPASS) {
        return -1;
    }
    xEventGroupSetBits(g_event_group, EVENT_GROUP_MLINK_REPORT_STATUS);
    return 0;
}

/**
 * @brief Checks the netif description if it contains specified prefix.
 * All netifs created withing common connect component are prefixed with the module TAG,
 * so it returns true if the specified netif is owned by this module
 */
bool example_is_our_netif(const char *prefix, esp_netif_t *netif)
{
    return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0;
}

static bool netif_desc_matches_with(esp_netif_t *netif, void *ctx)
{
    return strcmp(ctx, esp_netif_get_desc(netif)) == 0;
}

esp_netif_t *get_example_netif_from_desc(const char *desc)
{
    return esp_netif_find_if(netif_desc_matches_with, (void*)desc);
}

static esp_err_t esp_storage_init(void)
{
    esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        // NVS partition was truncated and needs to be erased
        // Retry nvs_flash_init
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    return ret;
}


// 定时器回调函数，每4小时调用一次PCF_hctosys()同步系统时间
static void PCF_TimerCallback( TimerHandle_t xTimer ){
    PCF_hctosys();
}

// 外部看门狗芯片喂狗，超时复位时间1.6S
static void WDT_TimerCallback( TimerHandle_t xTimer ){
    iot_board_led_toggle_state(BOARD_IO_LED_11);
}

static void IO_Sync_TimerCallback( TimerHandle_t xTimer ){
    // EVENT_GROUP_AI_SAMPLE
    xEventGroupSetBits(g_event_group,EVENT_GROUP_AI_SAMPLE);
}

/**
 * @brief App tick event generate.
 */
static void app_tick_timercb(TimerHandle_t timer)
{
	xEventGroupSetBits(g_event_group,EVENT_GROUP_APP_TICK);
}

#ifdef CONFIG_DUMP_SYSTEM_STATUS
#define TASK_MAX_COUNT 32
typedef struct {
    uint32_t ulRunTimeCounter;
    uint32_t xTaskNumber;
} taskData_t;

static taskData_t previousSnapshot[TASK_MAX_COUNT];
static int taskTopIndex = 0;
static uint32_t previousTotalRunTime = 0;

static taskData_t *getPreviousTaskData(uint32_t xTaskNumber)
{
    // Try to find the task in the list of tasks
    for (int i = 0; i < taskTopIndex; i++) {
        if (previousSnapshot[i].xTaskNumber == xTaskNumber) {
            return &previousSnapshot[i];
        }
    }
    // Allocate a new entry
    ESP_ERROR_CHECK(!(taskTopIndex < TASK_MAX_COUNT));
    taskData_t *result = &previousSnapshot[taskTopIndex];
    result->xTaskNumber = xTaskNumber;
    taskTopIndex++;
    return result;
}

static void _system_dump()
{
    uint32_t totalRunTime;

    TaskStatus_t taskStats[TASK_MAX_COUNT];
    uint32_t taskCount = uxTaskGetSystemState(taskStats, TASK_MAX_COUNT, &totalRunTime);
    ESP_ERROR_CHECK(!(taskTopIndex < TASK_MAX_COUNT));
    uint32_t totalDelta = totalRunTime - previousTotalRunTime;
    float f = 100.0 / totalDelta;
    // Dumps the the CPU load and stack usage for all tasks
    // CPU usage is since last dump in % compared to total time spent in tasks. Note that time spent in interrupts will be included in measured time.
    // Stack usage is displayed as nr of unused bytes at peak stack usage.

    ESP_LOGI(TAG, "Task dump");
    ESP_LOGI(TAG, "Load\tStack left\tName\tPRI");

    for (uint32_t i = 0; i < taskCount; i++) {
        TaskStatus_t *stats = &taskStats[i];
        taskData_t *previousTaskData = getPreviousTaskData(stats->xTaskNumber);

        uint32_t taskRunTime = stats->ulRunTimeCounter;
        float load = f * (taskRunTime - previousTaskData->ulRunTimeCounter);
        ESP_LOGI(TAG, "%.2f \t%" PRIu32 "\t%s %" PRIu32 "\t", load, stats->usStackHighWaterMark, stats->pcTaskName, (uint32_t)stats->uxBasePriority);

        previousTaskData->ulRunTimeCounter = taskRunTime;
    }
    ESP_LOGI(TAG, "Free heap=%d Free mini=%d bigst=%d, internal=%d bigst=%d",
             heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT),
             heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT),
             heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL),
             heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL));
    previousTotalRunTime = totalRunTime;
}
#endif

static void on_modem_event(void *arg, esp_event_base_t event_base,
                           int32_t event_id, void *event_data)
{
    if (event_base == MODEM_BOARD_EVENT) {
        if (event_id == MODEM_EVENT_SIMCARD_DISCONN) {
            ESP_LOGW(TAG, "Modem Board Event: SIM Card disconnected");
        } else if (event_id == MODEM_EVENT_SIMCARD_CONN) {
            ESP_LOGI(TAG, "Modem Board Event: SIM Card Connected");
        } else if (event_id == MODEM_EVENT_DTE_DISCONN) {
            ESP_LOGW(TAG, "Modem Board Event: USB disconnected");
        } else if (event_id == MODEM_EVENT_DTE_CONN) {
            ESP_LOGI(TAG, "Modem Board Event: USB connected");
        } else if (event_id == MODEM_EVENT_DTE_RESTART) {
            ESP_LOGW(TAG, "Modem Board Event: Hardware restart");
        } else if (event_id == MODEM_EVENT_DTE_RESTART_DONE) {
            ESP_LOGI(TAG, "Modem Board Event: Hardware restart done");
        } else if (event_id == MODEM_EVENT_NET_CONN) {
            ESP_LOGI(TAG, "Modem Board Event: Network connected");
        } else if (event_id == MODEM_EVENT_NET_DISCONN) {
            ESP_LOGW(TAG, "Modem Board Event: Network disconnected");
        } else if (event_id == MODEM_EVENT_WIFI_STA_CONN) {
            ESP_LOGI(TAG, "Modem Board Event: Station connected");
        } else if (event_id == MODEM_EVENT_WIFI_STA_DISCONN) {
            ESP_LOGW(TAG, "Modem Board Event: All stations disconnected");
        }
    }
}

#ifdef CONFIG_EXAMPLE_PING_NETWORK
static void on_ping_success(esp_ping_handle_t hdl, void *args)
{
    uint8_t ttl;
    uint16_t seqno;
    uint32_t elapsed_time, recv_len;
    ip_addr_t target_addr;
    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
    esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
    esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
    esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
    ESP_LOGI(TAG, "%"PRIu32" bytes from %s icmp_seq=%u ttl=%u time=%"PRIu32" ms", recv_len, ipaddr_ntoa(&target_addr), seqno, ttl, elapsed_time);
}

static void on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
    uint16_t seqno;
    ip_addr_t target_addr;
    esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
    esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
    ESP_LOGW(TAG, "From %s icmp_seq=%u timeout", ipaddr_ntoa(&target_addr), seqno);
    // Users can add logic to handle ping timeout
    // Add Wait or Reset logic
}
#endif

// static esp_netif_t *ap_netif;
void modem_4g_init(void)
{
#ifdef CONFIG_ESP32_S3_USB_OTG
    bsp_usb_mode_select_host();
    bsp_usb_host_power_mode(BSP_USB_HOST_POWER_MODE_USB_DEV, true);
#endif

    /* Waiting for modem powerup */
    ESP_LOGI(TAG, "====================================");
    ESP_LOGI(TAG, "     ESP 4G Cat.1 Wi-Fi Router");
    ESP_LOGI(TAG, "====================================");

    /* Initialize modem board. Dial-up internet */
    modem_config_t modem_config = MODEM_DEFAULT_CONFIG();
    /* Modem init flag, used to control init process */
#ifndef CONFIG_EXAMPLE_ENTER_PPP_DURING_INIT
    /* if Not enter ppp, modem will enter command mode after init */
    modem_config.flags |= MODEM_FLAGS_INIT_NOT_ENTER_PPP;
    /* if Not waiting for modem ready, just return after modem init */
    modem_config.flags |= MODEM_FLAGS_INIT_NOT_BLOCK;
#endif
    modem_config.handler = on_modem_event;
    modem_board_init(&modem_config);
// #ifdef CONFIG_EXAMPLE_ENABLE_WEB_ROUTER
//     modem_http_get_nvs_wifi_config(&s_modem_wifi_config);
//     modem_http_init(&s_modem_wifi_config);
// #endif
    // ap_netif = modem_wifi_ap_init();
    // assert(ap_netif != NULL);
    // ESP_ERROR_CHECK(modem_wifi_set(&s_modem_wifi_config));

//     uint32_t ap_dns_addr = 0;
//     while (1) {

// #if !defined(CONFIG_EXAMPLE_ENTER_PPP_DURING_INIT) || defined(CONFIG_MODEM_SUPPORT_SECONDARY_AT_PORT)
//         // if you want to send AT command during ppp network working, make sure the modem support secondary AT port,
//         // otherwise, the modem interface must switch to command mode before send command
//         int rssi = 0, ber = 0;
//         modem_board_get_signal_quality(&rssi, &ber);
//         ESP_LOGI(TAG, "rssi=%d, ber=%d", rssi, ber);
// #endif

//         // If manual DNS not defined, set DNS when got address, user better to add a queue to handle this
// #ifdef CONFIG_EXAMPLE_AUTO_UPDATE_DNS
//         esp_netif_dns_info_t dns;
//         modem_board_get_dns_info(ESP_NETIF_DNS_MAIN, &dns);
//         uint32_t _ap_dns_addr = dns.ip.u_addr.ip4.addr;
//         if (_ap_dns_addr != ap_dns_addr) {
//             modem_wifi_set_dns(ap_netif, _ap_dns_addr);
//             ap_dns_addr = _ap_dns_addr;
//             ESP_LOGI(TAG, "changed: ap dns addr (auto): %s", inet_ntoa(ap_dns_addr));
//         }
// #endif

// #ifdef CONFIG_EXAMPLE_PING_NETWORK
//         ESP_LOGI(TAG, "Ping addr %s Restart..", ping_addr_s);
//         esp_ping_start(ping);
// #endif

// #ifdef CONFIG_DUMP_SYSTEM_STATUS
//         _system_dump();
// #endif
//         vTaskDelay(pdMS_TO_TICKS(10000));
//     }
}



#if 1//LORA_E32_SUPPORT

static int e32_recv_handle(void *arg, const char *data, int size) {
    e32_handle_t handle = (e32_handle_t)arg;
    MDF_LOGI("e220_recv_handle, size: %d", size);
    // dump data
    for(int i=0;i<size;i++) {
        printf("%02x ", (uint8_t)data[i]);
    }
    printf("\n");
    return 0;
}

static void lora_init(lora_config_t *config) {

    // 判断串口是否已经安装过
    if(uart_is_driver_installed(UART_PORT_RF)) {
        ESP_LOGW(TAG, "uart%d is already installed", UART_NUM_2);
        return;
    }
    e32_set_mode(NULL, E32_MODE_NORMAL);
    vTaskDelay(100 / portTICK_RATE_MS);
    // 进入配置模式
    e32_set_mode(NULL, E32_MODE_SLEEP);
    vTaskDelay(100 / portTICK_RATE_MS);
    // 等待g_e32_aux_status==1，超时时间300ms
    // uint32_t start_time = xTaskGetTickCount();
    // while(g_e32_aux_status == 0 && (xTaskGetTickCount() - start_time) < 3000 / portTICK_RATE_MS) {
    //     vTaskDelay(10 / portTICK_RATE_MS);
    // }
    // if(g_e32_aux_status == 0) {
    //     ESP_LOGE(TAG, "e32 aux status timeout");
    //     e32_set_mode(NULL, E32_MODE_NORMAL);
    //     return;
    // }

    e32_port_config_t e32_port_config = {
       .uart_num = UART_PORT_RF,
       .rx_io_num = GPIO_RF_RXD,
       .tx_io_num = GPIO_RF_TXD,
       .aux_io_num = -1,
       .mode_io_num = -1,
       .baud_rate = 9600,
    };
    e32_handle_t e32_handle;
    e32_port_create(&e32_port_config, &e32_handle);
    e32_handle->recv_handle = e32_recv_handle;
    e32_handle->mode = E32_MODE_SLEEP;
    // 参数设置测试
    if(0 > e32_get_version(e32_handle, NULL)) {
        ESP_LOGE(TAG, "e32_get_version failed");
    }
    ESP_LOGI(TAG, "model: %"PRIX8", version: %"PRIX8", interface: %"PRIX8"", e32_handle->version.model, e32_handle->version.version, e32_handle->version.interface);
    vTaskDelay(100 / portTICK_RATE_MS);
    if(0 > e32_get_params(e32_handle, NULL)) {
        ESP_LOGE(TAG, "e32_get_params failed");
    }
    ESP_LOGI(TAG, "addr: %"PRIX16", speed: %"PRIX8", channel: %"PRIX8", option: %"PRIX8"", e32_handle->params.addr, e32_handle->params.speed, e32_handle->params.channel, e32_handle->params.option);
    vTaskDelay(100 / portTICK_RATE_MS);

    e32_handle->params.addr = config->addr;
    e32_handle->params.speed = 0b00011000 | (config->sp&0x07);
    e32_handle->params.channel = config->ch & 0x1F;
    e32_handle->params.option = 0b01000100 | (config->pwr&0x03);
    // if( 0 > e32_set_params(e32_handle, &e32_handle->params)) {
    //     ESP_LOGE(TAG, "e32_set_params failed");
    // }
    ESP_LOGI(TAG, "addr: %"PRIX16", speed: %"PRIX8", channel: %"PRIX8", option: %"PRIX8"", e32_handle->params.addr, e32_handle->params.speed, e32_handle->params.channel, e32_handle->params.option);
    vTaskDelay(100 / portTICK_RATE_MS);
    // 进入普通模式
    e32_set_mode(NULL, E32_MODE_NORMAL);
    vTaskDelay(100 / portTICK_RATE_MS);
    // while (1) {
    //     // 透传测试
    //     e32_port_write(e32_handle, (uint8_t *)"AT\r\n", 4);
    //     ESP_LOGI(TAG, "e32_uart_event_task");
    //     e32_uart_event_task(e32_handle,0);
    //     vTaskDelay(1000 / portTICK_RATE_MS);
    // }

    e32_port_destroy(&e32_handle);
}
#elif LORA_E220_400T30S_SUPPORT

static int e220_recv_handle(void *arg, const char *data, int size) {
    e220_400t30s_handle_t handle = (e220_400t30s_handle_t)arg;
    MDF_LOGI("e220_recv_handle, size: %d", size);
    // dump data
    for(int i=0;i<size;i++){
        printf("%02x ", (uint8_t)data[i]);
    }
    printf("\n");
    return 0;
}

static void lora_init(lora_config_t *config) {

    // 判断串口是否已经安装过
    if(uart_is_driver_installed(UART_PORT_RF)) {
        ESP_LOGW(TAG, "uart%d is already installed", UART_NUM_2);
        return;
    }
    // 进入配置模式
    e220_set_mode(NULL, E220_400T30S_MODE_SLEEP);

    e220_port_config_t e220_port_config = {
       .uart_num = UART_PORT_RF,
       .rx_io_num = GPIO_RF_RXD,
       .tx_io_num = GPIO_RF_TXD,
       .aux_io_num = -1,
       .mode_io_num = -1,
       .baud_rate = 9600,
    };
    e220_400t30s_handle_t e220_handle;
    e220_port_create(&e220_port_config, &e220_handle);
    e220_handle->recv_handle = e220_recv_handle;
    e220_handle->mode = E220_400T30S_MODE_SLEEP;
    vTaskDelay(100 / portTICK_RATE_MS);
    // 参数设置测试
    if(0 > e220_get_version(e220_handle, NULL)) {
        ESP_LOGE(TAG, "e220_get_version failed");
    }
    ESP_LOGI(TAG, "model: %"PRIX8", version: %"PRIX8", interface: %"PRIX8"", e220_handle->version.model, e220_handle->version.version, e220_handle->version.interface);
    vTaskDelay(100 / portTICK_RATE_MS);
    if(0 > e220_get_params(e220_handle, NULL)) {
        ESP_LOGE(TAG, "e220_get_params failed");
    }
    ESP_LOGI(TAG, "addr: %"PRIX16", speed: %"PRIX8", channel: %"PRIX8", option: %"PRIX8"", e220_handle->params.addr, e220_handle->params.speed, e220_handle->params.channel, e220_handle->params.option);
    vTaskDelay(100 / portTICK_RATE_MS);

    e220_handle->params.addr = config->addr;
    e220_handle->params.speed = 0b01100000 | (config->sp&0x07);
    e220_handle->params.reg1 = 0b01100000 | (config->pwr&0x03);
    e220_handle->params.channel = config->ch;
    // if( 0 > e220_set_params(e220_handle, &e220_handle->params)) {
    //     ESP_LOGE(TAG, "e220_set_params failed");
    // }
    ESP_LOGI(TAG, "addr: %"PRIX16", speed: %"PRIX8", channel: %"PRIX8", option: %"PRIX8"", e220_handle->params.addr, e220_handle->params.speed, e220_handle->params.channel, e220_handle->params.option);
    vTaskDelay(100 / portTICK_RATE_MS);
    // 进入普通模式
    e220_set_mode(NULL, E220_400T30S_MODE_NORMAL);
    vTaskDelay(100 / portTICK_RATE_MS);
    while (1) {
        // 透传测试
        e220_port_write(e220_handle, (uint8_t *)"AT\r\n", 4);
        ESP_LOGI(TAG, "e220_uart_event_task");
        e220_uart_event_task(e220_handle,0);
        vTaskDelay(1000 / portTICK_RATE_MS);
    }

    e220_port_destroy(&e220_handle);
}
#endif

static int subdev_cids_to_mb_rtu_descriptor(subdev_port_t *subdev, mb_parameter_descriptor_t *parameter_descriptor){
	// 判断输入参数是否合法
	if(subdev == NULL || parameter_descriptor == NULL){
		return ESP_FAIL;
	}
	int parameter_num = 0;
	// 遍历port->cids数组，将其转换为mb_parameter_descriptor_t结构体数组
	for(int i=0; i<subdev->cids_num; i++){
		int addr, func_code, reg_addr;
		subdev_cid_t *cid = &subdev->cids[i];

		if (3 != sscanf(cid->cid, "%d!%1d%d", &addr,&func_code,&reg_addr)){
			ESP_LOGI(TAG, "Invalid arg %s", cid->cid);
			continue;
		}
		if(!(func_code == 0 || func_code==1 || func_code==3 || func_code==4)){
			ESP_LOGI(TAG, "Invalid arg %s", cid->cid);
			continue;
		}
        reg_addr -= 1; // PLC地址格式
        if(reg_addr < 0) {
            ESP_LOGI(TAG, "reg_addr %d < 0", reg_addr);
            continue;
        }
		parameter_descriptor[i].cid = i;
		asprintf(&parameter_descriptor[i].param_key, "%d", i);
		parameter_descriptor[i].param_units = "_";
		parameter_descriptor[i].mb_slave_addr = addr;
		switch(func_code){
			case 4:
				parameter_descriptor[i].mb_param_type = MB_PARAM_HOLDING;
				break;
			case 3:
				parameter_descriptor[i].mb_param_type = MB_PARAM_INPUT;
				break;
			case 1:
				parameter_descriptor[i].mb_param_type = MB_PARAM_DISCRETE;
				break;
			case 0:
				parameter_descriptor[i].mb_param_type = MB_PARAM_COIL;
				break;
		}
		
		parameter_descriptor[i].mb_reg_start = reg_addr;
		parameter_descriptor[i].param_offset = i;
		if(strcmp(cid->type, "u16") == 0 || strcmp(cid->type, "i16") == 0){
			parameter_descriptor[i].mb_size = 1;
			parameter_descriptor[i].param_type = PARAM_TYPE_U16;
			parameter_descriptor[i].param_size = sizeof(uint16_t);
		} else if(strcmp(cid->type, "u32") == 0 || strcmp(cid->type, "i32") == 0){
			parameter_descriptor[i].mb_size = 2;
			parameter_descriptor[i].param_type = PARAM_TYPE_U32;
			parameter_descriptor[i].param_size = sizeof(uint32_t);
		} else if(strcmp(cid->type, "u32CDAB") == 0 || (strcmp(cid->type, "i32CDAB") == 0)){
			parameter_descriptor[i].mb_size = 2;
			parameter_descriptor[i].param_type = PARAM_TYPE_U32_CDAB;
			parameter_descriptor[i].param_size = sizeof(uint32_t);
		} else if(strcmp(cid->type, "float") == 0){
			parameter_descriptor[i].mb_size = 2;
			parameter_descriptor[i].param_type = PARAM_TYPE_FLOAT;
			parameter_descriptor[i].param_size = sizeof(float);
		}  else if(strcmp(cid->type, "fCDAB") == 0){
			parameter_descriptor[i].mb_size = 2;
			parameter_descriptor[i].param_type = PARAM_TYPE_FLOAT_CDAB;
			parameter_descriptor[i].param_size = sizeof(float);
		}
		parameter_descriptor[i].param_opts.min = 0;
		parameter_descriptor[i].param_opts.max = 0;
		parameter_descriptor[i].param_opts.step = 0;
		parameter_descriptor[i].access = PAR_PERMS_READ_WRITE_TRIGGER;

		parameter_num++;
		// 打印parameter_descriptor[i]结构体中所有的数据
		ESP_LOGI(TAG, "parameter_descriptor[%d]", i);
		ESP_LOGI(TAG, "\t param_key %s", parameter_descriptor[i].param_key);
		ESP_LOGD(TAG, "\t mb_slave_addr %d", parameter_descriptor[i].mb_slave_addr);
		ESP_LOGD(TAG, "\t mb_param_type %d", parameter_descriptor[i].mb_param_type);
		ESP_LOGD(TAG, "\t mb_reg_start %d", parameter_descriptor[i].mb_reg_start);
		ESP_LOGD(TAG, "\t mb_size %d", parameter_descriptor[i].mb_size);
		ESP_LOGD(TAG, "\t param_offset %d", parameter_descriptor[i].param_offset);
		ESP_LOGD(TAG, "\t param_size %d", parameter_descriptor[i].param_size);
	}
	// 返回转换成功的parameter_descriptor数组个数
	return parameter_num;
}


static void subdev_init(void)  {
    // 载入串口参数
    mlink_dt_load_store();
    dt_channel_store_t dt_cfg[2]={DT_CONFIG_DEFAULT(9600), DT_CONFIG_DEFAULT(9600)};
    dt_get_config_info(DT_CH_0_UART, &dt_cfg[0]);
    dt_get_config_info(DT_CH_1_UART, &dt_cfg[1]);
    ESP_LOGI(TAG, "Serial %d baud_rate %d p %d", 0, dt_cfg[0].baud_rate, dt_cfg[0].parity);
    ESP_LOGI(TAG, "Serial %d baud_rate %d p %d", 1, dt_cfg[1].baud_rate, dt_cfg[1].parity);
    int serial_gpio[2][4] = {
        {UART_PORT_485,GPIO_485_TXD,GPIO_485_RXD,GPIO_485_DIR},
        {UART_PORT_485_2,GPIO_485_2_TXD,GPIO_485_2_RXD,GPIO_485_2_DIR}
    };
    // 如果串口2用于Lora
    lora_config_t lora_config = LORA_DEFAULT_CONFIG;
    lora_load_config(&lora_config);
    if(lora_config.en) {
        ESP_LOGI(TAG, "Lora en %d", lora_config.en);
        lora_init(&lora_config);
        serial_gpio[1][0] = UART_PORT_RF;
        serial_gpio[1][1] = GPIO_RF_TXD;
        serial_gpio[1][2] = GPIO_RF_RXD;
        serial_gpio[1][3] = -1;

        iot_board_led_set_state(BOARD_IO_LoRaLED, true); // 打开LoRa指示灯
    }

    device_subdev_0_init(); // 初始化子设备0
    mlink_subdev_load_form_flash(); // 从flash中加载子设备配置
    // 根据node配置初始化串口
    for(int i=0;i<SUBDEV_PORT_MAX_NUM; i++){

		subdev_port_t *subdev = mlink_get_subdev_handle_by_index(i);
		if(subdev == NULL){
			continue;
		}
        if(subdev->cids_num == 0)
            continue;
        ESP_LOGI(TAG, "subdev_port %d protocol %d", i, subdev->protocol);
        
        if(subdev->protocol == SUBDEV_PROTOCOL_DLT645_07 
            || subdev->protocol == SUBDEV_PROTOCOL_DLT645_97 
            || subdev->protocol == SUBDEV_PROTOCOL_CJT188_04){
            uint8_t uart_num = subdev->info_union.serial.uart_num;
            if( uart_num > 1 ){
                ESP_LOGW(TAG, "uart_num %d is out of range", uart_num);
                continue;
            }
            // 判断串口是否已经安装过
            if(uart_is_driver_installed(serial_gpio[uart_num][0])) {
                ESP_LOGW(TAG, "uart%d is already installed", serial_gpio[uart_num][0]);
                continue;
            }
            dlt645_port_config_t dlt645_port_config = {
                .uart_num = serial_gpio[uart_num][0],
                .baud_rate = dt_cfg[uart_num].baud_rate,
                .data_bits = dt_cfg[uart_num].data_bits,
                .parity = dt_cfg[uart_num].parity,
                .stop_bits = dt_cfg[uart_num].stop_bits,
                .mode = UART_MODE_RS485_HALF_DUPLEX,
                .tx_io_num = serial_gpio[uart_num][1],
                .rx_io_num = serial_gpio[uart_num][2],
                .rts_io_num = serial_gpio[uart_num][3],
                .cts_io_num = -1
            };

            //dlt645 硬件层初始化
            dlt645_t *dlt645_handle = calloc(1, sizeof(dlt645_t));
            assert(dlt645_handle);

            dlt645_handle->port_data = subdev;
            subdev->port_data = dlt645_handle;
            dlt645_port_create(&dlt645_port_config, dlt645_handle);
            xTaskCreate(subdev_dlt645_scan_task, "dlt645_scan", 4 * 1024, dlt645_handle, 6, NULL);

        }
        else if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_RTU || subdev->protocol == SUBDEV_PROTOCOL_MODBUS_ASCII){
            uint8_t uart_num = subdev->info_union.serial.uart_num;
            if( uart_num > 1 ){
                ESP_LOGW(TAG, "uart_num %d is out of range", uart_num);
                continue;
            }
            // 判断串口是否已经安装过
            if(uart_is_driver_installed(serial_gpio[uart_num][0])) {
                ESP_LOGW(TAG, "uart%d is already installed", serial_gpio[uart_num][0]);
                continue;
            }
            mb_app_t *mb_handle = calloc(1, sizeof( mb_app_t ));
            assert(mb_handle);
            mb_handle->port_data = subdev;
            subdev->port_data = mb_handle;

            // 转换cid数组为mb_parameter_descriptor_t数组
            mb_handle->parameter_descriptor = calloc(subdev->cids_num, sizeof(mb_parameter_descriptor_t));
            mb_handle->parameter_num = subdev_cids_to_mb_rtu_descriptor(subdev, mb_handle->parameter_descriptor);

            if(mb_handle->parameter_num == 0){
                free(mb_handle->parameter_descriptor);
                free(mb_handle);
                continue;
            }
            // Modbus RTU 硬件层初始化
            mb_app_config_t mb_cfg={
                .uart_num = serial_gpio[uart_num][0],
                .baud_rate = dt_cfg[uart_num].baud_rate,
                .parity = dt_cfg[uart_num].parity,
                .tx_io_num = serial_gpio[uart_num][1],
                .rx_io_num = serial_gpio[uart_num][2],
                .rts_io_num = serial_gpio[uart_num][3],
            };
            // Initialize and start Modbus controller
            mb_handle->comm.port = mb_cfg.uart_num;
            if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_RTU){
                mb_handle->comm.mode = MB_MODE_RTU;
            }else if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_ASCII){
                mb_handle->comm.mode = MB_MODE_ASCII;
            }else{
                ESP_LOGW(TAG, "protocol %d is not supported", subdev->protocol);
            }
            mb_handle->comm.baudrate = mb_cfg.baud_rate;
            mb_handle->comm.parity = mb_cfg.parity;

            if(ESP_OK == mb_master_serial_create(&mb_cfg, mb_handle)){
                xTaskCreate(mb_app_task, "mb_task", 3 * 1024, mb_handle, 6, NULL);     
            } else {
                ESP_LOGW(TAG, "mb_master_serial_create failed");
            }
        }else if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_TCP || subdev->protocol == SUBDEV_PROTOCOL_MODBUS_UDP){
            xEventGroupWaitBits(g_event_group, EVENT_GROUP_ETH_GOT_IP|EVENT_GROUP_STA_GOT_IP, pdFALSE, pdFALSE, pdMS_TO_TICKS(30000));

            mb_app_t *mb_handle = calloc(1, sizeof( mb_app_t ));
            assert(mb_handle);
            mb_handle->port_data = subdev;
            subdev->port_data = mb_handle;

            mb_handle->parameter_descriptor = calloc(subdev->cids_num, sizeof(mb_parameter_descriptor_t));
            mb_handle->parameter_num = subdev_cids_to_mb_rtu_descriptor(subdev, mb_handle->parameter_descriptor);

            if(mb_handle->parameter_num == 0){
                free(mb_handle->parameter_descriptor);
                free(mb_handle);
                continue;
            }
            // slave_ip_address_table，多申请一个空间，最后一个必须是NULL
            char** slave_ip_address_table = calloc(subdev->info_union.modbus_tcp.slave_num+1, sizeof( char* ));
            assert(slave_ip_address_table);
            for(int j=0; j<subdev->info_union.modbus_tcp.slave_num; j++){
                slave_ip_address_table[j]=subdev->info_union.modbus_tcp.slave_ip[j];
                ESP_LOGI(TAG, "Modbus TCP slave_ip[%d] %s", j, slave_ip_address_table[j]);
            }
            mb_handle->comm.ip_addr = slave_ip_address_table;
            mb_handle->comm.ip_port = subdev->info_union.modbus_tcp.slave_port;
            mb_handle->comm.ip_addr_type = MB_IPV4;
            if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_TCP){
                mb_handle->comm.ip_mode = MB_MODE_TCP;
            }else if(subdev->protocol == SUBDEV_PROTOCOL_MODBUS_UDP){
                mb_handle->comm.ip_mode = MB_MODE_UDP;
            }else{
                ESP_LOGW(TAG, "protocol %d is not supported", subdev->protocol);
            }

            mb_handle->comm.ip_netif_ptr = (void*)gateway_default_netif;
            if(ESP_OK == mb_master_tcp_create(mb_handle)){
                xTaskCreate(mb_app_task, "mb_task", 3 * 1024, mb_handle, 6, NULL); 
            } else {
                ESP_LOGW(TAG, "mb_master_tcp_create failed");
            }
        }
        else if(subdev->protocol == SUBDEV_PROTOCOL_NONE) {
            static TimerHandle_t timer = NULL;
            if(timer == NULL){
                if(subdev->poll_interval < 100){
                    subdev->poll_interval = 100;
                }
                timer = xTimerCreate("IO_SYNC", pdMS_TO_TICKS(subdev->poll_interval), pdTRUE, (void*)0, IO_Sync_TimerCallback);
                if(timer != NULL){
                    xTimerStart(timer, 0);
                }                
            }
        }
    }
}

// Event handler for Ethernet
static void eth_event_handler(void *arg, esp_event_base_t event_base,
                              int32_t event_id, void *event_data)
{
    /* we can get the ethernet driver handle from event data */
    esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;

    switch (event_id) {
        case ETHERNET_EVENT_CONNECTED:
            ESP_LOGI(TAG, "Ethernet Link Up");
            break;

        case ETHERNET_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "Ethernet Link Down");
            break;

        case ETHERNET_EVENT_START:
            ESP_LOGI(TAG, "Ethernet Started");
            break;

        case ETHERNET_EVENT_STOP:
            ESP_LOGI(TAG, "Ethernet Stopped");
            break;

        default:
            break;
    }
}

static void eth_got_ip_event_handler(void *arg, esp_event_base_t event_base,
                                int32_t event_id, void *event_data)
{
    ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
    const esp_netif_ip_info_t *ip_info = &event->ip_info;

	
	switch (event_id) {
        case IP_EVENT_ETH_GOT_IP:
			ESP_LOGI(TAG, "Ethernet Got IP Address");
			ESP_LOGI(TAG, "~~~~~~~~~~~");
			ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
			ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
			ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
			ESP_LOGI(TAG, "~~~~~~~~~~~");
            xEventGroupSetBits(g_event_group, EVENT_GROUP_ETH_GOT_IP);
            break;
		case IP_EVENT_ETH_LOST_IP:
			ESP_LOGI(TAG, "Ethernet LOST IP");
            xEventGroupClearBits(g_event_group, EVENT_GROUP_ETH_GOT_IP);
			break;
	}
}

static void sta_got_ip_event_handler(void *arg, esp_event_base_t event_base,
                                int32_t event_id, void *event_data)
{
    ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
    const esp_netif_ip_info_t *ip_info = &event->ip_info;

	switch (event_id) {
        case IP_EVENT_STA_GOT_IP:
			ESP_LOGI(TAG, "STA Got IP Address");
			ESP_LOGI(TAG, "~~~~~~~~~~~");
			ESP_LOGI(TAG, "STAIP:" IPSTR, IP2STR(&ip_info->ip));
			ESP_LOGI(TAG, "STAMASK:" IPSTR, IP2STR(&ip_info->netmask));
			ESP_LOGI(TAG, "STAGW:" IPSTR, IP2STR(&ip_info->gw));
			ESP_LOGI(TAG, "~~~~~~~~~~~");
            xEventGroupSetBits(g_event_group, EVENT_GROUP_STA_GOT_IP);
            break;
		case IP_EVENT_STA_LOST_IP:
			ESP_LOGI(TAG, "STA LOST IP");
            xEventGroupClearBits(g_event_group, EVENT_GROUP_STA_GOT_IP);
			break;
	}
}

void my_esp_bridge_create_all_netif(void)
{
    int ip_mode = 1;
    char nvs_dhcpc_key[16];

    ESP_LOGI(TAG, "esp-iot-bridge version: %d.%d.%d", IOT_BRIDGE_VER_MAJOR, IOT_BRIDGE_VER_MINOR, IOT_BRIDGE_VER_PATCH);
    char *default_netif_name = device_get_default_netif_name(NULL);
    ESP_LOGI(TAG, "default_netif_name %s", default_netif_name);

#if defined(CONFIG_BRIDGE_DATA_FORWARDING_NETIF_SOFTAP)
    ip_mode = 1;
    sprintf(nvs_dhcpc_key, "%s.md", "WIFI_AP_DEF");
    mdf_info_load(nvs_dhcpc_key, &ip_mode, sizeof(ip_mode));
    esp_bridge_create_softap_netif(NULL, NULL, true, ip_mode);
#if defined(CONFIG_BRIDGE_WIFI_PMF_DISABLE)
    esp_wifi_disable_pmf_config(WIFI_IF_AP);
    ESP_LOGI(TAG, "DHCPS Restart, deauth all station");
    esp_wifi_deauth_sta(0);
#endif
#endif

#if defined(CONFIG_BRIDGE_EXTERNAL_NETIF_ETHERNET) || defined(CONFIG_BRIDGE_NETIF_ETHERNET_AUTO_WAN_OR_LAN)
    ip_mode = 1;
    sprintf(nvs_dhcpc_key, "%s.md", "ETH_WAN");
    mdf_info_load(nvs_dhcpc_key, &ip_mode, sizeof(ip_mode));
    if(ip_mode == 0){
        ESP_LOGI(TAG, "eth dhcpc enable");
        esp_bridge_create_eth_netif(NULL, NULL, false, false);
    }
    else {
        esp_netif_ip_info_t ip_info_from_nvs;
        bool conflict_check = true;
        if (esp_bridge_load_ip_info_from_nvs("ETH_WAN", &ip_info_from_nvs, &conflict_check) == ESP_OK) {

        }
        else {
            // 默认IP地址
            ip_info_from_nvs.ip.addr = ipaddr_addr("192.168.2.123");
            ip_info_from_nvs.gw.addr = ipaddr_addr("192.168.2.1");
            ip_info_from_nvs.netmask.addr = ipaddr_addr("255.255.255.0");
            conflict_check = true;
        }
        ESP_LOGI(TAG, "custom eth ip_info_from_nvs.ip.addr %s", ip4addr_ntoa(&ip_info_from_nvs.ip));
        ESP_LOGI(TAG, "custom eth ip_info_from_nvs.gw.addr %s", ip4addr_ntoa(&ip_info_from_nvs.gw));
        ESP_LOGI(TAG, "custom eth ip_info_from_nvs.netmask.addr %s", ip4addr_ntoa(&ip_info_from_nvs.netmask));
        esp_bridge_create_eth_netif(&ip_info_from_nvs, NULL, false, false);
    }
    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &eth_got_ip_event_handler, NULL));
#endif

#if defined(CONFIG_BRIDGE_EXTERNAL_NETIF_STATION)
    // if(strcmp(default_netif_name, "WIFI_STA_DEF") == 0){
        ip_mode = 1;
        sprintf(nvs_dhcpc_key, "%s.md", "WIFI_STA_DEF");
        mdf_info_load(nvs_dhcpc_key, &ip_mode, sizeof(ip_mode));
        if(ip_mode == 0){
            ESP_LOGI(TAG, "sta dhcpc enable");
            esp_bridge_create_station_netif(NULL, NULL, false, false);
        }
        else {
            esp_netif_ip_info_t ip_info_from_nvs;
            bool conflict_check = true;
            if (esp_bridge_load_ip_info_from_nvs("WIFI_STA_DEF", &ip_info_from_nvs, &conflict_check) == ESP_OK) {

            }
            else {
                // 默认IP地址
                ip_info_from_nvs.ip.addr = ipaddr_addr("192.168.3.123");
                ip_info_from_nvs.gw.addr = ipaddr_addr("192.168.3.1");
                ip_info_from_nvs.netmask.addr = ipaddr_addr("255.255.255.0");
                conflict_check = true;
            }
            ESP_LOGI(TAG, "custom sta ip_info_from_nvs.ip.addr %s", ip4addr_ntoa(&ip_info_from_nvs.ip));
            ESP_LOGI(TAG, "custom sta ip_info_from_nvs.gw.addr %s", ip4addr_ntoa(&ip_info_from_nvs.gw));
            ESP_LOGI(TAG, "custom sta ip_info_from_nvs.netmask.addr %s", ip4addr_ntoa(&ip_info_from_nvs.netmask));
            esp_bridge_create_station_netif(&ip_info_from_nvs, NULL, false, false);
        }
        // esp_bridge_create_station_netif(NULL, NULL, false, false);
        ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &sta_got_ip_event_handler, NULL));
        #if defined(CONFIG_BRIDGE_WIFI_PMF_DISABLE)
            esp_wifi_disable_pmf_config(WIFI_IF_STA);
        #endif
    // }
#endif

    // 设置默认网卡
    gateway_default_netif = esp_netif_get_handle_from_ifkey(default_netif_name);
    if(gateway_default_netif){
        esp_netif_set_default_netif(gateway_default_netif);
    }

#ifdef CONFIG_EXAMPLE_ENABLE_WEB_ROUTER
    modem_http_get_nvs_wifi_config(&s_modem_wifi_config);
    modem_http_init(&s_modem_wifi_config);
#endif

#if defined(CONFIG_BRIDGE_DATA_FORWARDING_NETIF_SOFTAP)
    wifi_config_t wifi_cfg = {
        .ap = {
            .ssid = CONFIG_BRIDGE_SOFTAP_SSID,
            .password = CONFIG_BRIDGE_SOFTAP_PASSWORD,
            .ssid_hidden = s_modem_wifi_config.ssid_hidden,
        }
    };
    strcpy((char *)wifi_cfg.ap.ssid, s_modem_wifi_config.ssid);
    strcpy((char *)wifi_cfg.ap.password, s_modem_wifi_config.password);
    esp_bridge_wifi_set_config(WIFI_IF_AP, &wifi_cfg);
#endif
#if defined(CONFIG_BRIDGE_EXTERNAL_NETIF_STATION)
    esp_wifi_connect();
#endif

}

static esp_err_t mlink_handle_set_netif_ip(mlink_handle_data_t *handle_data) {
	esp_err_t ret	  = ESP_OK;
	esp_netif_ip_info_t ip_info;
	char netif_name[16];
	char static_ip[16];
	char static_netmask[16];
	char static_gw[16];
	int mode;

	ret = mlink_json_parse(handle_data->req_data, "netif", netif_name);
	MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");

    esp_netif_t *netif = esp_netif_get_handle_from_ifkey(netif_name);
    if( netif == NULL){
        ESP_LOGI(TAG, "netif %s not found", netif_name);
        return ESP_FAIL;
    }

	// if(strcmp(netif_name, "WIFI_STA_DEF") == 0 || strcmp(netif_name, "ETH_WAN") == 0){
        ret = mlink_json_parse(handle_data->req_data, "mode", &mode);
        MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");
	// }

	if(mode == 1 || strcmp(netif_name, "WIFI_AP_DEF") == 0) {
		ret = mlink_json_parse(handle_data->req_data, "static_ip", static_ip);
		MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");

		ret = mlink_json_parse(handle_data->req_data, "static_netmask", static_netmask);
		MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");

		ret = mlink_json_parse(handle_data->req_data, "static_gw", static_gw);
		MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");
		ip_info.ip.addr = ipaddr_addr(static_ip);
		ip_info.netmask.addr = ipaddr_addr(static_netmask);
		ip_info.gw.addr = ipaddr_addr(static_gw);

        // if(ESP_OK != esp_bridge_save_ip_info_to_nvs(netif_name, &ip_info, true)){
        //     ESP_LOGI(TAG, "save ip info failed");
        //     return ESP_FAIL;
        // }
		if(ESP_OK != esp_bridge_netif_set_ip_info(netif, &ip_info, true, false)){
            ESP_LOGI(TAG, "set ip info failed");
            return ESP_FAIL;
        }

	} else {
        // if(ESP_OK != esp_bridge_erase_ip_info_from_nvs(netif_name)){
        //     ESP_LOGI(TAG, "erase ip info failed");
        //     return ESP_FAIL;
        // }
		if(ESP_OK != esp_bridge_netif_set_ip_info(netif, NULL, true, false)){
            ESP_LOGI(TAG, "set ip info failed");
            return ESP_FAIL;
        }
	}

    // 保存enable到nvs
    strcat(netif_name, ".md");
    ret = mdf_info_save(netif_name, &mode, sizeof(mode));

	return ret;
}

static esp_err_t mlink_handle_get_netif_ip(mlink_handle_data_t *handle_data) {
	int ret = ESP_OK;
	esp_netif_ip_info_t ip_info;
	char netif_name[16]={0};
	char static_ip[16]={0};
	char static_netmask[16]={0};
	char static_gw[16]={0};
	int mode;
	bool conflict_check;
	ret = mlink_json_parse(handle_data->req_data, "netif", netif_name);
	MDF_ERROR_CHECK(ret != MDF_OK, MDF_FAIL, "Parse the json formatted string");
	// if(strcmp(netif_name, "WIFI_STA_DEF") != 0 && strcmp(netif_name, "ETH_WAN") != 0){
	// 	return ESP_FAIL;
	// }
	if(ESP_OK == esp_bridge_load_ip_info_from_nvs(netif_name, &ip_info,&conflict_check)){
		// ip_info.ip.addr转字符串
		ipaddr_ntoa_r(&ip_info.ip, static_ip, sizeof(static_ip));
		ipaddr_ntoa_r(&ip_info.netmask, static_netmask, sizeof(static_netmask));
		ipaddr_ntoa_r(&ip_info.gw, static_gw, sizeof(static_gw));
	}

    mlink_json_pack(&handle_data->resp_data, "netif", netif_name);
	mlink_json_pack(&handle_data->resp_data, "static_ip", static_ip);
	mlink_json_pack(&handle_data->resp_data, "static_netmask", static_netmask);
 	mlink_json_pack(&handle_data->resp_data, "static_gw", static_gw);
    
    strcat(netif_name, ".md");
    if(mdf_info_load(netif_name, &mode, sizeof(mode)) == ESP_OK){
        handle_data->req_size = mlink_json_pack(&handle_data->resp_data, "mode", mode);
    } else {
        handle_data->req_size = mlink_json_pack(&handle_data->resp_data, "mode", 1);
    }

	return ret;
}

static void system_reset(void) {
    int ret = mdf_info_erase(MDF_SPACE_NAME);
    if(ret != ESP_OK) {
        ESP_LOGE(TAG, "Erase MDF space ret %d", ret);
    }

    mdf_info_erase_space("netif_ip_info");
    mdf_info_erase_space("memory");

    iot_board_led_set_state(BOARD_IO_BZ, true);
    vTaskDelay(pdMS_TO_TICKS(50));
    iot_board_led_set_state(BOARD_IO_BZ, false);
    vTaskDelay(pdMS_TO_TICKS(50));
    iot_board_led_set_state(BOARD_IO_BZ, true);
    vTaskDelay(pdMS_TO_TICKS(50));
    iot_board_led_set_state(BOARD_IO_BZ, false);
    vTaskDelay(pdMS_TO_TICKS(50));

    esp_restart();
}

static mdf_err_t event_loop_cb(mdf_event_loop_t event, void *ctx)
{
    MDF_LOGD("event_loop_cb, event: 0x%x", (unsigned int)event);
    mdf_err_t ret = MDF_OK;

    switch (event) {
 
        case MDF_EVENT_MUPGRADE_STARTED:
            MDF_LOGI("Enter upgrade mode");
            break;

        case MDF_EVENT_MUPGRADE_STATUS: {
            MDF_LOGI("The upgrade progress is: %d%%", (int)ctx);
            break;
        }

        case MDF_EVENT_MUPGRADE_FINISH:
            MDF_LOGI("Upgrade completed waiting for restart");
            break;

        case MDF_EVENT_MLINK_SYSTEM_RESET:
            MDF_LOGW("Erase information saved in flash and system restart");

            ret = mdf_info_erase(MDF_SPACE_NAME);
            MDF_ERROR_BREAK(ret != 0, "Erase the information");

            system_reset();
            break;

        case MDF_EVENT_MLINK_SYSTEM_REBOOT:
            MDF_LOGW("Restart PRO and APP CPUs");
            iot_board_led_set_state(BOARD_IO_BZ, true);
            vTaskDelay(pdMS_TO_TICKS(50));
            iot_board_led_set_state(BOARD_IO_BZ, false);
            vTaskDelay(pdMS_TO_TICKS(50));
            esp_restart();
            break;

        case MDF_EVENT_MLINK_SET_STATUS:
			xEventGroupSetBits(g_event_group, EVENT_GROUP_MLINK_SET_STATUS);
            break;

		case MDF_EVENT_MLINK_TRIGGER_TRIGGER:
		{
			char *resp_data 				  = NULL;
			int resp_size;
			char *str=NULL;
			str = mlink_trigger_get_name(ctx);
			if(str){
				resp_size = mlink_json_pack(&resp_data, "name", str);
			}
			str = mlink_trigger_get_alias(ctx);
			if(str){
				resp_size = mlink_json_pack(&resp_data, "alias", str);
			}
			resp_size = mlink_json_pack(&resp_data, "type", "trigger_event");
			mlink_mqtt_write(&resp_data, resp_size);
			MDF_FREE(resp_data);
			break;
		}
		case MDF_EVENT_MLINK_TRIGGER_SCENES:
		{
			char *resp_data 				  = NULL;
			int resp_size;
			char *str=NULL;
			str = mlink_scenes_get_name(ctx);
			if(str){
				resp_size = mlink_json_pack(&resp_data, "name", str);
			}
			str = mlink_scenes_get_alias(ctx);
			if(str){
				resp_size = mlink_json_pack(&resp_data, "alias", str);
			}
			resp_size = mlink_json_pack(&resp_data, "type", "trigger_scenes");
			mlink_mqtt_write(&resp_data, resp_size);
			MDF_FREE(resp_data);
			break;
		}

		case MDF_EVENT_CUSTOM_MQTT_CONNECTED:
			MDF_LOGI("MQTT connect");
            mqtt_heartbeat_send();
			break;

		case MDF_EVENT_CUSTOM_MQTT_DISCONNECTED:
			MDF_LOGI("MQTT disconnected");
			break;

        default:
            break;
    }

	// led_show_key_release(event);

    return MDF_OK;
}


static void main_loop_task(void *arg)
{
    // mdf_err_t ret = MDF_OK;
	uint32_t sec_count = 0;
	uint32_t report_count = 0;
	time_t nowtime=0;
	struct tm timeinfo;
	TimerHandle_t app_tick_timer = xTimerCreate("app_tick", pdMS_TO_TICKS(1000),
                                       true, NULL, app_tick_timercb);
	xTimerStart(app_tick_timer, 0);

    while (1) {
 		EventBits_t event_bits = xEventGroupWaitBits(g_event_group, 
			EVENT_GROUP_MLINK_SET_STATUS | EVENT_GROUP_APP_TICK | EVENT_GROUP_MLINK_REPORT_STATUS | EVENT_GROUP_AI_SAMPLE, 
			pdTRUE, pdFALSE, portMAX_DELAY);
		
		if(event_bits & EVENT_GROUP_MLINK_SET_STATUS){
			MDF_LOGD("EVENT_GROUP_MLINK_SET_STATUS");
			mlink_trigger_handle(MLINK_COMMUNICATE_MESH);
		}

		if(event_bits & EVENT_GROUP_AI_SAMPLE){
            // ESP_LOGI(TAG, "EVENT_GROUP_AI_SAMPLE");
            subdev_io_scan_task(NULL);
		}
        
		if(event_bits & EVENT_GROUP_MLINK_REPORT_STATUS){
			MDF_LOGD("EVENT_GROUP_MLINK_REPORT_STATUS");
            if (mesh_mqtt_is_connect()) {
                report_cid_data_t subdev_cid_data;
                if(pdTRUE == xQueueReceive(g_report_cid_queue, &subdev_cid_data, 0)){
                    MDF_LOGD("report_cid: %s", subdev_cid_data.subdev_cid->cid);
                    subdev_cid_value_report(subdev_cid_data.subdev_port, subdev_cid_data.subdev_cid);
                }
            }else{
                vTaskDelay(pdMS_TO_TICKS(50));
            }
            if(false == xQueueIsQueueEmptyFromISR(g_report_cid_queue)){
                xEventGroupSetBits(g_event_group, EVENT_GROUP_MLINK_REPORT_STATUS);
            }
		}
		if(event_bits & EVENT_GROUP_APP_TICK){
			//MDF_LOGD("EVENT_GROUP_APP_TICK");
			sec_count++;
            report_count++;
			time(&nowtime);
			localtime_r(&nowtime, &timeinfo);

			if(sec_count % 600 == 0) {
                #ifdef CONFIG_MQTT_ENABLE
				mqtt_heartbeat_send();
                #endif
			}

			if(sec_count % 20 == 0){
				trigger_timming_handle(nowtime);
			}

			// report task
			// if(sec_count > 30){
			// 	if(mesh_mqtt_is_connect()){
			// 		// 周期上报状态
			// 		if(device_get_reboot_time() > 0 && report_count > device_get_reboot_time()){
            //             report_count = 0;
			// 			for(int i=0;i<SUBDEV_PORT_MAX_NUM;i++){
			// 				subdev_cid_report(i);
			// 			}
			// 		}
			// 	}
			// }

			// 设备定时重启
			int time_of_day = timeinfo.tm_hour * 3600 + timeinfo.tm_min * 60 + timeinfo.tm_sec;
			int reboot_time = device_get_reboot_time() > 0 ? device_get_reboot_time() : 0;
			if(reboot_time > 0 && time_of_day > reboot_time && time_of_day < reboot_time+3 ){
				vTaskDelay(3000 / portTICK_RATE_MS);
				esp_restart();
			}

		}
    }
	
	xTimerStop(app_tick_timer, 0);
    vTaskDelete(NULL);
}

static void button_press_down_cb(void *button_handle, void *usr_data)
{
	int btn_index = board_button_handle_to_io(button_handle);
    ESP_LOGD(TAG, "BTN%d: BUTTON_PRESS_DOWN", btn_index);
    switch(btn_index){
        case BOARD_IO_DI_1:
            device_set_di_status(DEVICE_IO_DI01, 0);
        break;
        case BOARD_IO_DI_2:
            device_set_di_status(DEVICE_IO_DI02, 0);
        break;
        case BOARD_IO_DI_3:
            device_set_di_status(DEVICE_IO_DI03, 0);
        break;
        case BOARD_IO_DI_4:
            device_set_di_status(DEVICE_IO_DI04, 0);
        break;
        case BOARD_IO_E32_INT:
            g_e32_aux_status = false;
        break;
        // case BOARD_IO_POWIN_DEC:
        //     device_set_power_source(0);
        // break;
    }
	// mdf_event_loop_send(MDF_EVENT_BUTTON_PRESS_DOWN, (void *)btn_index);
}

static void button_press_up_cb(void *button_handle, void *usr_data)
{
	int btn_index = board_button_handle_to_io((button_handle_t)button_handle);
    ESP_LOGD(TAG, "BTN%d: BUTTON_PRESS_UP", btn_index);
    switch(btn_index){
        case BOARD_IO_DI_1:
            device_set_di_status(DEVICE_IO_DI01, 1);
        break;
        case BOARD_IO_DI_2:
            device_set_di_status(DEVICE_IO_DI02, 1);
        break;
        case BOARD_IO_DI_3:
            device_set_di_status(DEVICE_IO_DI03, 1);
        break;
        case BOARD_IO_DI_4:
            device_set_di_status(DEVICE_IO_DI04, 1);
        break;
        case BOARD_IO_E32_INT:
            g_e32_aux_status = true;
        break;
        // case BOARD_IO_POWIN_DEC:
        //     device_set_power_source(1);
        // break;
    }
	// mdf_event_loop_send(MDF_EVENT_BUTTON_PRESS_UP, (void *)btn_index);
}

static void user_button_single_click_cb(void *button_handle, void *usr_data)
{
	int btn_index = board_button_handle_to_io(button_handle);
    ESP_LOGI(TAG, "BTN%d: SINGLE CLICK", btn_index);
    switch(btn_index){
        case BOARD_IO_USER_KEY:

        break;
    }
	// mdf_event_loop_send(MDF_EVENT_BUTTON_PRESS_DOWN, (void *)btn_index);
}

static void user_button_double_click_cb(void *button_handle, void *usr_data)
{
	int btn_index = board_button_handle_to_io((button_handle_t)button_handle);
    ESP_LOGI(TAG, "BTN%d: DOUBLE CLICK", btn_index);
    switch(btn_index){
        case BOARD_IO_USER_KEY:
            mdf_event_loop_send(MDF_EVENT_MLINK_SYSTEM_REBOOT, NULL);
        break;
    }
	// mdf_event_loop_send(MDF_EVENT_BUTTON_PRESS_UP, (void *)btn_index);
}

static void user_button_long_click_cb(void *button_handle, void *usr_data)
{
	int btn_index = board_button_handle_to_io((button_handle_t)button_handle);
    ESP_LOGI(TAG, "BTN%d: DOUBLE CLICK", btn_index);
    switch(btn_index){
        case BOARD_IO_USER_KEY:
            mdf_event_loop_send(MDF_EVENT_MLINK_SYSTEM_RESET, NULL);
        break;
    }
	// mdf_event_loop_send(MDF_EVENT_BUTTON_PRESS_UP, (void *)btn_index);
}

void app_main(void)
{
    // esp_log_level_set("*", ESP_LOG_INFO);
    // esp_log_level_set("w5500.mac", ESP_LOG_DEBUG);
    // esp_log_level_set("w5500.phy", ESP_LOG_DEBUG);
  
    TimerHandle_t timer = NULL;

    esp_storage_init();

    if (restart_count_get() >= 30) {
        MDF_LOGW("Erase information saved in flash");
        mdf_info_erase(MDF_SPACE_NAME);
        mdf_info_erase_space("netif_ip_info");
        mdf_info_erase_space("memory");
    }

	setenv("TZ", "UTC-8", 1);
    tzset();

    g_event_group = xEventGroupCreate();
    g_report_cid_queue = xQueueCreate(10, sizeof(report_cid_data_t));

    /* Init and register system/core components */
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    MDF_ERROR_ASSERT(mdf_event_loop_init(event_loop_cb));

	device_load_record();
	DEVICE_TID = DEVICE_TID_DTU;
    ESP_ERROR_CHECK(iot_board_init());
    WDT_TimerCallback(NULL);
    timer = xTimerCreate("WDT feed", pdMS_TO_TICKS(200), pdTRUE, (void*)0, WDT_TimerCallback);
    if(timer != NULL){
        xTimerStart(timer, 0);
    }

    iot_board_led_set_state(BOARD_IO_4GPOW_EN, true); // 4G power on
    iot_board_led_set_state(BOARD_IO_5VPOW_EN, true); // 5V power on
    iot_board_led_set_state(BOARD_IO_BZ, false); // on buzzer
    iot_board_led_set_state(BOARD_IO_LoRaLED, false); // 关闭LoRa指示灯
    iot_board_led_set_state(BOARD_IO_LOWBT_LED, false); // 关闭
    
    iot_board_button_register_cb(BOARD_IO_DI_1, BUTTON_PRESS_DOWN, button_press_down_cb);
    iot_board_button_register_cb(BOARD_IO_DI_1, BUTTON_PRESS_UP, button_press_up_cb);
    iot_board_button_register_cb(BOARD_IO_DI_2, BUTTON_PRESS_DOWN, button_press_down_cb);
    iot_board_button_register_cb(BOARD_IO_DI_2, BUTTON_PRESS_UP, button_press_up_cb);
    iot_board_button_register_cb(BOARD_IO_DI_3, BUTTON_PRESS_DOWN, button_press_down_cb);
    iot_board_button_register_cb(BOARD_IO_DI_3, BUTTON_PRESS_UP, button_press_up_cb);
    iot_board_button_register_cb(BOARD_IO_DI_4, BUTTON_PRESS_DOWN, button_press_down_cb);
    iot_board_button_register_cb(BOARD_IO_DI_4, BUTTON_PRESS_UP, button_press_up_cb);
    // iot_board_button_register_cb(BOARD_IO_CHG_STA, BUTTON_PRESS_DOWN, button_press_down_cb);
    // iot_board_button_register_cb(BOARD_IO_CHG_STA, BUTTON_PRESS_UP, button_press_up_cb);
    iot_board_button_register_cb(BOARD_IO_USER_KEY, BUTTON_SINGLE_CLICK, user_button_single_click_cb);
    iot_board_button_register_cb(BOARD_IO_USER_KEY, BUTTON_DOUBLE_CLICK, user_button_double_click_cb);
    iot_board_button_register_cb(BOARD_IO_USER_KEY, BUTTON_LONG_PRESS_START, user_button_long_click_cb);
    iot_board_button_register_cb(BOARD_IO_E32_INT, BUTTON_PRESS_DOWN, button_press_down_cb);
    iot_board_button_register_cb(BOARD_IO_E32_INT, BUTTON_PRESS_UP, button_press_up_cb);
    // iot_board_button_register_cb(BOARD_IO_POWIN_DEC, BUTTON_PRESS_DOWN, button_press_down_cb);
    // iot_board_button_register_cb(BOARD_IO_POWIN_DEC, BUTTON_PRESS_UP, button_press_up_cb);

    // 初始化ADC
    driver_adc_init();

    // 初始化PCF8563时钟
    const i2c_lk8563_config_t lk8563_config ={
        .dev_addr = PCF8563_I2C_ADDR,
        .clk_speed = 400000,
    };
    if(ESP_OK != PCF_Init(iot_board_get_handle(BOARD_I2C0_ID), &lk8563_config, &i2c_dev_lk8563)){
        ESP_LOGE(TAG, "PCF_Init failed");
    }else{
        PCF_hctosys();
        // 创建一个定时器，每4小时调用一次PCF_hctosys();
        timer = xTimerCreate("PCF_systohc", pdMS_TO_TICKS(3600 * 1000), pdTRUE, (void*)0, PCF_TimerCallback);
        if(timer != NULL){
            xTimerStart(timer, 0);
        }
    }

    // 网络初始化
    my_esp_bridge_create_all_netif();

#ifdef CONFIG_EXAMPLE_PING_NETWORK
    ip_addr_t target_addr;
    memset(&target_addr, 0, sizeof(target_addr));
    char *ping_addr_s = NULL;
#ifdef CONFIG_EXAMPLE_PING_MANUAL_ADDR
    // Ping users defined address
    ping_addr_s = CONFIG_EXAMPLE_PING_MANUAL_ADDR;
#else
    // otherwise Ping DNS server
    esp_netif_dns_info_t dns2;
    modem_board_get_dns_info(ESP_NETIF_DNS_MAIN, &dns2);
    ping_addr_s = ip4addr_ntoa((ip4_addr_t *)(&dns2.ip.u_addr.ip4));
#endif
    esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
    ipaddr_aton(ping_addr_s, &target_addr);
    ping_config.target_addr = target_addr;
    ping_config.timeout_ms = CONFIG_EXAMPLE_PING_TIMEOUT;
    ping_config.task_stack_size = 4096;
    ping_config.count = 1;

    /* set callback functions */
    esp_ping_callbacks_t cbs = {
        .on_ping_success = on_ping_success,
        .on_ping_timeout = on_ping_timeout,
        .on_ping_end = NULL,
        .cb_args = NULL,
    };
    esp_ping_handle_t ping;
    esp_ping_new_session(&ping_config, &cbs, &ping);
#endif

	MDF_ERROR_ASSERT(device_controller_init());
    MDF_ERROR_ASSERT(mlink_ota_init());
    MDF_ERROR_ASSERT(mlink_trigger_init());
    MDF_ERROR_ASSERT(mlink_scenes_init());
	MDF_ERROR_ASSERT(mlink_mqtt_init());
	MDF_ERROR_ASSERT(mlink_dt_init());
	MDF_ERROR_ASSERT(mlink_sntp_init());
	MDF_ERROR_ASSERT(mlink_lora_init());
	MDF_ERROR_ASSERT(mlink_subdev_handle_init());
	mlink_set_handle("set_netif_ip", mlink_handle_set_netif_ip);
	mlink_set_handle("get_netif_ip", mlink_handle_get_netif_ip);
	// 同步DO状态,同步两次，第一次可能失败
	for(int i=0; i<DEVICE_IO_DO_MAX; i++){
        ESP_LOGI(TAG, "DO%d: %d", i, device_get_do_status(i));
		device_set_do_status(i, device_get_do_status(i));
	}
    for(int i=0; i<DEVICE_IO_DO_MAX; i++){
        ESP_LOGI(TAG, "DO%d: %d", i, device_get_do_status(i));
		device_set_do_status(i, device_get_do_status(i));
	}

    mesh_root_task_init();

	xTaskCreate(main_loop_task, "main_loop", 4 * 1024, NULL, 1, NULL);
	xTaskCreate(mlink_handle_request_noresp_task, "mlink_handle", 4 * 1024, NULL, 1, NULL);

    subdev_init(); // 初始化子设备

    sntp_config_t sntp_config = {0, ""};
    sntp_load_config( &sntp_config );
    if(sntp_config.enable){
        ESP_LOGI(TAG, "SNTP enabled");
        sntp_start_obtain_time();
    }

#if 1
    char *default_netif_name = device_get_default_netif_name(NULL);
    if(strcmp(default_netif_name, "PPP_DEF") == 0){
        // 强制复位4G模块
        iot_board_led_set_state(BOARD_IO_ML307R_REST, true); // reset 4g mode
        vTaskDelay(600 / portTICK_RATE_MS);
        iot_board_led_set_state(BOARD_IO_ML307R_REST, false); // free reset io
        modem_4g_init();
        // 设置默认网卡
        gateway_default_netif = esp_netif_get_handle_from_ifkey(default_netif_name);
        if(gateway_default_netif){
            esp_netif_set_default_netif(gateway_default_netif);
        }
    }
#endif

    iot_board_led_set_state(BOARD_IO_BZ, true); // on buzzer
    vTaskDelay(pdMS_TO_TICKS(100));
    iot_board_led_set_state(BOARD_IO_BZ, false); // off buzzer

    while (1) {

#ifdef CONFIG_EXAMPLE_PING_NETWORK
        ESP_LOGI(TAG, "Ping addr %s Restart..", ping_addr_s);
        esp_ping_start(ping);
#endif

#ifdef CONFIG_DUMP_SYSTEM_STATUS
        _system_dump();
#endif
        vTaskDelay(pdMS_TO_TICKS(10000));
    }

}
